/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.vertigo.fever;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.Log;

/**
 * Render a pair of tumbling cubes.
 */

class GameRenderer 
implements GLSurfaceView.Renderer {
//extends GLSurfaceView implements Renderer { 

	private boolean mTranslucentBackground;

	// public static float yAngle = 0f;

	public Vector accel = new Vector();

	private World world;
	public Ship ship = new Ship();
	public Camera camera = new Camera(ship.position);
	int level = 0;
	private Context context;
	

	private float[] lightAmbient = {0.5f, 0.5f, 0.5f, 1.0f};
	private float[] lightDiffuse = {1.0f, 1.0f, 1.0f, 1.0f};
	private float[] lightPosition = {0.0f, 0.0f, 2.0f, 1.0f};
	
	private FloatBuffer lightAmbientBuffer;
	private FloatBuffer lightDiffuseBuffer;
	private FloatBuffer lightPositionBuffer;

	public GameRenderer(Context context, boolean useTranslucentBackground) {
		//super(context);
		this.context = context;
		mTranslucentBackground = useTranslucentBackground;
		world = new World();
		reset();
		

		//
		ByteBuffer byteBuf = ByteBuffer.allocateDirect(lightAmbient.length * 4);
		byteBuf.order(ByteOrder.nativeOrder());
		lightAmbientBuffer = byteBuf.asFloatBuffer();
		lightAmbientBuffer.put(lightAmbient);
		lightAmbientBuffer.position(0);
		
		byteBuf = ByteBuffer.allocateDirect(lightDiffuse.length * 4);
		byteBuf.order(ByteOrder.nativeOrder());
		lightDiffuseBuffer = byteBuf.asFloatBuffer();
		lightDiffuseBuffer.put(lightDiffuse);
		lightDiffuseBuffer.position(0);
		
		byteBuf = ByteBuffer.allocateDirect(lightPosition.length * 4);
		byteBuf.order(ByteOrder.nativeOrder());
		lightPositionBuffer = byteBuf.asFloatBuffer();
		lightPositionBuffer.put(lightPosition);
		lightPositionBuffer.position(0);
		
	}
	
	public void reset() {
		accel.x = 0;
		accel.y = 0;
		accel.z = 0;
		ship.reset();
	}


	// "update" method
	public void onDrawFrame(GL10 gl) {
		updateState();
		// processInput();
		// updateAI();
		updatePhysics();
		// updateAnimations();
		// updateSound();
		updateGraphics(gl);
	}

	public void updateState() {

		if (ship.position.y > 10) {
			// game over
			reset();
		}
	}

	private void updatePhysics() {
		// check on a tile (only checking x & z)
		Tile t = world.getTile(level, ship.position);

		// clear alert
		ship.alert = false;

		// no tile
		if (t == null) {
			ship.alert = true;
			accel.y++; // falling
		}
		// above or below tile
		else if (ship.position.y != 0) {
			if (Math.abs(ship.position.y) < Math.abs(accel.y)) {
				accel.y = 0;
				ship.position.y = 0; // snap to position
			} else {
				accel.y++; // falling
			}
		}
		// jump tile
		else if (t.getCode() == 'S') {
			accel.y -= 2; // jumping
			if (accel.z < 2) accel.z += 2; // forward
		}

		// jump tile
		else if (t.getCode() == 'F') {
			if (accel.z < 2) accel.z += 2; // forward
		}
		// jump tile
		else if (t.getCode() == 'L') {
			if (accel.x > -1) accel.x -= 1; // left
		}
		// jump tile
		else if (t.getCode() == 'R') {
			if (accel.x < 1) accel.x += 1; // right
		}

		// move camera and ship
		ship.position.add(accel);
		ship.rotate = +accel.z + 15;
		float inc = 0.25f;
		// reset movement
		if (accel.x > 0) {
			accel.x -= inc;
			Log.i("x",Float.toString(accel.x));
		}
		if (accel.y > 0) {
			accel.y -= inc;
			Log.i("y",Float.toString(accel.y));
		}
		if (accel.z > 0) {
			accel.z -= inc;
			Log.i("z",Float.toString(accel.z));
		}

		if (accel.x < 0) {
			accel.x += inc;
			Log.i("x",Float.toString(accel.x));
		}
		if (accel.y < 0) {
			accel.y += inc;
			Log.i("y",Float.toString(accel.y));
		}
		if (accel.z < 0) {
			accel.z += inc;
			Log.i("z",Float.toString(accel.z));
		}

	}

	public void updateGraphics(GL10 gl) {

		/*
		 * Usually, the first thing one might want to do is to clear the screen.
		 * The most efficient way of doing this is to use glClear().
		 */

		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		/*
		 * Now we're ready to draw some 3D objects
		 */
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
		camera.translateToOigin(gl);

		// Lighting
		//gl.glEnable(gl.GL_LIGHTING);
		gl.glEnable(gl.GL_COLOR_MATERIAL);
//		gl.glEnable(gl.GL_AMBIENT_AND_DIFFUSE);
//		gl.glShadeModel(gl.GL_SMOOTH);
		
		//Really Nice Perspective Calculations
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 
		

		
		float mcolor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
		//gl.glMaterialfv(gl.GL_FRONT, gl.GL_AMBIENT_AND_DIFFUSE, mcolor, 0);

		
		ship.draw(gl);

		camera.translate(gl);

		int rowId = 0;
		world.draw(gl, rowId, level);

		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) {
		gl.glViewport(0, 0, width, height);

		/*
		 * Set our projection matrix. This doesn't have to be done each time we
		 * draw, but usually a new projection needs to be set when the viewport
		 * is resized.
		 */

		float ratio = (float) width / height;
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glFrustumf(-ratio, ratio, -1, 1, 1, 40);
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//		/*
//		 * By default, OpenGL enables features that improve quality but reduce
//		 * performance. One might want to tweak that especially on software
//		 * renderer.
//		 */
//		gl.glDisable(GL10.GL_DITHER);
//
//		/*
//		 * Some one-time OpenGL initialization can be made here probably based
//		 * on features of this particular context
//		 */
//		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
//
//		if (mTranslucentBackground) {
//			gl.glClearColor(0, 0, 0, 0);
//		} else {
//			gl.glClearColor(1, 1, 1, 1);
//		}
//		gl.glEnable(GL10.GL_CULL_FACE);
//		gl.glShadeModel(GL10.GL_SMOOTH);
//		gl.glEnable(GL10.GL_DEPTH_TEST);
		
//		float ambientLight[] =  { 10, 10, 10, 1 };
//		float diffuseLight[] =  { 100, 100, 100, 1 };
//		float specularLight[] = { 0, 0, 0, 1 };
//		float position[] =      { -10,-4,-16 };
//
//		gl.glLightfv(gl.GL_LIGHT0, gl.GL_AMBIENT, ambientLight, 0);
//		gl.glLightfv(gl.GL_LIGHT0, gl.GL_DIFFUSE, diffuseLight, 0);
//		gl.glLightfv(gl.GL_LIGHT0, gl.GL_SPECULAR, specularLight, 0);
//		gl.glLightfv(gl.GL_LIGHT0, gl.GL_POSITION, position, 0);

		//And there'll be light!
		//gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbientBuffer);		//Setup The Ambient Light ( NEW )
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuseBuffer);		//Setup The Diffuse Light ( NEW )
		gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);	//Position The Light ( NEW )
		gl.glEnable(GL10.GL_LIGHT0);	
		
		gl.glEnable(gl.GL_LIGHT0);

		gl.glDisable(GL10.GL_DITHER);				//Disable dithering ( NEW )
		gl.glEnable(GL10.GL_TEXTURE_2D);			//Enable Texture Mapping
		gl.glShadeModel(GL10.GL_SMOOTH); 			//Enable Smooth Shading
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); 	//Black Background
		gl.glClearDepthf(1.0f); 					//Depth Buffer Setup
		gl.glEnable(GL10.GL_DEPTH_TEST); 			//Enables Depth Testing
		gl.glDepthFunc(GL10.GL_LEQUAL); 			//The Type Of Depth Testing To Do
		
		// TODO make this generic for all tiles
		TileCrate.loadGLTexture(gl, this.context);
		
		gl.glEnable(GL10.GL_LIGHTING);
	}
}
